﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.Core.DomainModel;
using MethodWorx.CMS.Workflow;
using MethodWorx.CMS.Membership;

namespace MethodWorx.CMS.Content
{
    public class Page : AggregateRoot
    {
        public Page()
            : this(p => new Page[] { }, p => new ContentItem[] { })
        {
        }

        public Page(Func<Page, IEnumerable<Page>> childPagesLoader, Func<Page, IEnumerable<ContentItem>> childContentLoader)
        {
            this.ChildPages = new ChildPageCollection(this, childPagesLoader);
            this.ChildContent = new ChildContentCollection(this, childContentLoader);
            this.DateCreated = DateTime.Now;
            this.Status = ContentStatus.Unpublished;
        }

        /// <summary>
        /// Name of the page
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Description of the page
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// Parent page (unless we are in a folder)
        /// </summary>
        public virtual Page Parent { get; set; }
        /// <summary>
        /// Page template
        /// </summary>
        public virtual PageTemplate Template { get; set; }

        /// <summary>
        /// This is the folder we are in (if we are not a child of page)
        /// </summary>
        public virtual Folder Folder { get; set; }

        public string Tags { get; set; }

        public ChildPageCollection ChildPages { get; set; }
        public ChildContentCollection ChildContent { get; set; }

        public virtual PageProperty[] Properties { get; set; }
        public virtual WorkflowConfiguration[] Workflows { get; set; }
        public virtual WorkflowConfiguration[] PublishedWorkflows { get; set; }

        public string PublishedCss { get; set; }
        public string PublishedScript { get; set; }

        public virtual User CreatedBy { get; set; }

        public string CreatedByUsername
        {
            get { return this.CreatedBy.Username; }
        }

        public virtual WorkflowConfiguration[] GetWorkflowConfiguration(bool preview)
        {
            if (preview)
            {
                //
                //  else return the combination of both workflows and template workflows
                List<WorkflowConfiguration> combined = new List<WorkflowConfiguration>();
                combined.AddRange(this.Workflows ?? new WorkflowConfiguration[] {});
                combined.AddRange(this.Template.Workflows ?? new WorkflowConfiguration[] { });

                return combined.ToArray();
            }
            else
            {
                //  only return the published workflows
                return this.PublishedWorkflows;
            }
        }

        public string Path
        {
            get
            {
                Page page = this;
                StringBuilder sb = new StringBuilder();
                while (page != null)
                {
                    sb.Insert(0, "/" + page.Name);
                    page = page.Parent;
                }
                return sb.ToString();
            }
        }

        public string PathWithoutRoot
        {
            get
            {
                Page page = this;
                StringBuilder sb = new StringBuilder();
                while (page != null)
                {
                    if(page.Parent != null)
                        sb.Insert(0, "/" + page.Name);
                    page = page.Parent;
                }
                return sb.ToString().TrimStart('/');
            }
        }

        public int Level
        {
            get
            {
                int level = -1;
                var page = this;
                while(page != null)
                {
                    level++;
                    page = page.Parent;
                }
                return level;
            }
        }

        public string Url
        {
            get
            {
                Page page = this;
                StringBuilder sb = new StringBuilder();
                while (page != null && page.Parent != null)
                {
                    sb.Insert(0, "/" + page.Name);
                    page = page.Parent;
                }
                if (sb.Length == 0)
                    sb.Append("/");

                return sb.ToString().TrimStart('/');
            }
        }

        public virtual PageProperty[] PublishedProperties { get; set; }
        public ContentStatus Status { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime? DatePublished { get; set; }
        public string PublishedTemplate { get; set; }
        public virtual Dictionary<string, string> PublishedPlaceholders { get { return null;  } }

        public string GetCss()
        {
            return this.Template.GetCss();
        }

        public string GetScript()
        {
            return this.Template.GetScript();
        }

        public WorkflowConfiguration AddWorkflow(string name, string invokeOnHttpMethod, string invokeOnPageEvent)
        {
            if (string.IsNullOrEmpty(name))
                throw new BusinessRuleException("Name", BusinessRuleException.REQUIRED);

            if (this.Workflows.Where(w => w.Name.ToLower() == name.ToLower()).Any())
                throw new BusinessRuleException("Name", BusinessRuleException.NOT_UNIQUE);
            
            List<WorkflowConfiguration> workflows = this.Workflows.ToList();
            var config = new WorkflowConfiguration()
                {
                    Name = name,
                    InvokeOnHttpMethod = invokeOnHttpMethod,
                    InvokeOnPageEvent = invokeOnPageEvent,
                    StartingStep = null
                };
            workflows.Add(config);

            this.Workflows = workflows.ToArray();

            return config;
        }

        public IEnumerable<Page> AllChildPages
        {
            get
            {
                foreach (var child in this.GetChildren(this))
                    yield return child;
            }
        }

        private IEnumerable<Page> GetChildren(Page parent)
        {
            foreach(var child in parent.ChildPages)
            {
                yield return child;
                foreach (var subChild in GetChildren(child))
                {
                    yield return subChild;
                }
            }
        }

        public bool CanCreateContentItemFromTemplate(ContentTemplate template)
        {
            var constraint = this.Template.ContentTemplateConstraints.FirstOrDefault(t => t.RelatedTemplate == template);
            if (constraint == null)
                return false;
            else
            {
                //
                //  ok, we have a constraint, do we have "room" to create here
                if (constraint.MaximumAllowed.HasValue)
                {
                    if(this.ChildContent.Where(c => c.Template == template).Count() >= constraint.MaximumAllowed.Value)
                        return false;
                }
            }
            
            return true;
        }

        public bool CanCreatePageFromTemplate(PageTemplate template)
        {
            var constraint = this.Template.PageTemplateConstraints.FirstOrDefault(t => t.RelatedTemplate == template);
            if (constraint == null)
                return false;
            else
            {
                //
                //  ok, we have a constraint, do we have "room" to create here
                if (constraint.MaximumAllowed.HasValue)
                {
                    if (this.ChildPages.Where(c => c.Template == template).Count() >= constraint.MaximumAllowed.Value)
                        return false;
                }
            }

            return true;
        }

        public void RemoveWorkflow(WorkflowConfiguration config)
        {
            if (this.Workflows != null)
                this.Workflows = this.Workflows.Where(w => w != config).ToArray();
        }

    
    }
}
